// ============================================================================
// E2E 测试工具函数
// ============================================================================

import { Page, Locator, expect } from '@playwright/test'

/**
 * 等待页面加载完成
 */
export async function waitForPageLoad(page: Page, timeout = 30000) {
  await page.waitForLoadState('networkidle', { timeout })
  await page.waitForSelector('[data-testid="app-loaded"]', { timeout })
}

/**
 * 等待元素可见
 */
export async function waitForVisible(locator: Locator, timeout = 5000) {
  await expect(locator).toBeVisible({ timeout })
}

/**
 * 等待元素隐藏
 */
export async function waitForHidden(locator: Locator, timeout = 5000) {
  await expect(locator).toBeHidden({ timeout })
}

/**
 * 安全点击元素
 */
export async function safeClick(locator: Locator, options?: { timeout?: number; force?: boolean }) {
  await locator.waitFor({ state: 'visible', timeout: options?.timeout || 5000 })
  await locator.click({ force: options?.force })
}

/**
 * 安全填写输入框
 */
export async function safeFill(locator: Locator, value: string, options?: { timeout?: number; clear?: boolean }) {
  await locator.waitFor({ state: 'visible', timeout: options?.timeout || 5000 })
  
  if (options?.clear !== false) {
    await locator.clear()
  }
  
  await locator.fill(value)
}

/**
 * 等待并获取文本内容
 */
export async function getTextContent(locator: Locator, timeout = 5000): Promise<string> {
  await locator.waitFor({ state: 'visible', timeout })
  const text = await locator.textContent()
  return text || ''
}

/**
 * 滚动到元素
 */
export async function scrollToElement(locator: Locator) {
  await locator.scrollIntoViewIfNeeded()
}

/**
 * 等待加载完成
 */
export async function waitForLoading(page: Page, timeout = 10000) {
  // 等待加载指示器消失
  const loadingSelectors = [
    '[data-testid="loading-spinner"]',
    '.loading',
    '[aria-label="loading"]'
  ]
  
  for (const selector of loadingSelectors) {
    try {
      await page.waitForSelector(selector, { state: 'hidden', timeout: 1000 })
    } catch {
      // 忽略超时错误，继续检查下一个选择器
    }
  }
}

/**
 * 模拟键盘输入
 */
export async function typeText(page: Page, selector: string, text: string, options?: { delay?: number }) {
  const element = page.locator(selector)
  await element.waitFor({ state: 'visible' })
  await element.type(text, { delay: options?.delay || 50 })
}

/**
 * 截图并保存
 */
export async function takeScreenshot(page: Page, name: string, options?: { fullPage?: boolean }) {
  const timestamp = new Date().toISOString().replace(/[:.]/g, '-')
  const filename = `${name}-${timestamp}.png`
  
  await page.screenshot({
    path: `test-results/screenshots/${filename}`,
    fullPage: options?.fullPage || false
  })
  
  return filename
}

/**
 * 等待网络请求完成
 */
export async function waitForNetworkIdle(page: Page, timeout = 5000) {
  await page.waitForLoadState('networkidle', { timeout })
}

/**
 * 模拟文件上传
 */
export async function uploadFile(page: Page, selector: string, filePath: string) {
  const fileInput = page.locator(selector)
  await fileInput.setInputFiles(filePath)
}

/**
 * 等待并验证URL
 */
export async function waitForURL(page: Page, url: string | RegExp, timeout = 5000) {
  await page.waitForURL(url, { timeout })
}

/**
 * 获取元素属性
 */
export async function getAttribute(locator: Locator, attribute: string): Promise<string | null> {
  await locator.waitFor({ state: 'visible' })
  return await locator.getAttribute(attribute)
}

/**
 * 检查元素是否存在
 */
export async function elementExists(page: Page, selector: string, timeout = 1000): Promise<boolean> {
  try {
    await page.waitForSelector(selector, { timeout })
    return true
  } catch {
    return false
  }
}

/**
 * 等待元素数量
 */
export async function waitForElementCount(page: Page, selector: string, count: number, timeout = 5000) {
  await page.waitForFunction(
    ({ selector, count }) => document.querySelectorAll(selector).length === count,
    { selector, count },
    { timeout }
  )
}

/**
 * 模拟拖拽操作
 */
export async function dragAndDrop(page: Page, sourceSelector: string, targetSelector: string) {
  const source = page.locator(sourceSelector)
  const target = page.locator(targetSelector)
  
  await source.dragTo(target)
}

/**
 * 等待并点击按钮
 */
export async function clickButton(page: Page, text: string, options?: { exact?: boolean; timeout?: number }) {
  const button = page.getByRole('button', { 
    name: text, 
    exact: options?.exact || false 
  })
  
  await button.waitFor({ state: 'visible', timeout: options?.timeout || 5000 })
  await button.click()
}

/**
 * 等待并填写表单字段
 */
export async function fillFormField(page: Page, label: string, value: string, options?: { exact?: boolean }) {
  const field = page.getByLabel(label, { exact: options?.exact || false })
  await field.waitFor({ state: 'visible' })
  await field.clear()
  await field.fill(value)
}

/**
 * 验证通知消息
 */
export async function verifyNotification(page: Page, message: string, type?: 'success' | 'error' | 'warning' | 'info') {
  const notificationSelector = type ? `[data-testid="notification-${type}"]` : '[data-testid^="notification-"]'
  const notification = page.locator(notificationSelector).filter({ hasText: message })
  
  await expect(notification).toBeVisible({ timeout: 5000 })
  
  // 等待通知消失
  await expect(notification).toBeHidden({ timeout: 10000 })
}

/**
 * 模拟移动端视口
 */
export async function setMobileViewport(page: Page) {
  await page.setViewportSize({ width: 375, height: 667 })
}

/**
 * 模拟桌面端视口
 */
export async function setDesktopViewport(page: Page) {
  await page.setViewportSize({ width: 1280, height: 720 })
}

/**
 * 等待动画完成
 */
export async function waitForAnimation(page: Page, selector?: string, timeout = 2000) {
  if (selector) {
    const element = page.locator(selector)
    await element.waitFor({ state: 'visible' })
  }
  
  // 等待CSS动画和过渡完成
  await page.waitForTimeout(timeout)
}

/**
 * 验证表格数据
 */
export async function verifyTableData(page: Page, tableSelector: string, expectedData: string[][]) {
  const table = page.locator(tableSelector)
  await table.waitFor({ state: 'visible' })
  
  const rows = table.locator('tbody tr')
  const rowCount = await rows.count()
  
  expect(rowCount).toBe(expectedData.length)
  
  for (let i = 0; i < expectedData.length; i++) {
    const row = rows.nth(i)
    const cells = row.locator('td')
    
    for (let j = 0; j < expectedData[i].length; j++) {
      const cell = cells.nth(j)
      await expect(cell).toContainText(expectedData[i][j])
    }
  }
}
